﻿#include "firmwareWidget.h"
#include <QtCore/QDebug>
#include <app.h>
#include <QMessageBox>
#include <QStringListModel>
#include <QStandardItemModel>
#include <QDir>
#include <QFileDialog>
#include <QTextCodec>
#include <QTextstream>
#include <QSettings>

const char basePath[] = "./firmwareWarehouse";
const char gitPath[] = "https://gitee.com/LeviathanClass/firmwareWarehouse.git";

FirmwareWidget::FirmwareWidget(ConnectThread *thread, Config *config):
	_config(config),
	_rep(nullptr),
	_remote(nullptr),
	_connectThread(thread),
	_selectBranch(""),
	_partLength(MAX_PACKET_LENGTH),
	_binStream(0),
	_nCountBinNumber(0),
	_startDownloadTimerId(0),
	_reconnectTimerId(0),
	_reconnectPortName(""),
	_newestBranch(""),
	_reconnectBaudrate(0),
	_currentBinNumber(0),
	_totalBinLength(0) {
	//加上这一句才会自动释放对象
	this->setAttribute(Qt::WA_DeleteOnClose);
	ui.setupUi(this);
	//初始化QtAwesome
	_awesome = new QtAwesome();
	_awesome->initFontAwesome();
	QVariantMap options;
	//初始化颜色
	options.insert("color", QColor(0x94ffff));
	options.insert("color-active", QColor(0x94ffff));
	//按键添加图标
	ui.getLocalVersionBtn->setIcon(_awesome->icon(fa::book, options));
	ui.getLastedBinBtn->setIcon(_awesome->icon(fa::newspapero, options));
	ui.getPreviousBinBtn->setIcon(_awesome->icon(fa::listol, options));
	ui.loadCustomBtn->setIcon(_awesome->icon(fa::foldero, options));
	ui.chooseFirmwareBtn->setIcon(_awesome->icon(fa::checksquareo, options));
	ui.downloadBtn->setIcon(_awesome->icon(fa::download, options));
	//控件失能
	firmwareRecord(fa::ban, tr("当前未加载固件"));
	ui.downloadBtn->setEnabled(false);
	setGetVersionEnable(false);
	//检测文件夹是否存在并自动创建
	autoCreateDir(basePath);
	//git远程初始化
	git_libgit2_init();
	int error = git_remote_create_anonymous(&_remote, nullptr, gitPath);   /* URL */
	if (error < 0) {
		const git_error *e = giterr_last();
		qDebug() << "Error: " << error << " / " << e->klass << " : " << e->message;
		delete e;
	}

	createJsonControlMapping();
}

void FirmwareWidget::createJsonControlMapping() {
	//对应叶下增加相应槽
	_config->addJsonDirSlot("/system/bootVersion", this, SLOT(bootVersionDecode(JsonDirContent)));
	_config->addJsonDirSlot("/system/appVersion", this, SLOT(appVersionDecode(JsonDirContent)));
}

FirmwareWidget::~FirmwareWidget() {

}

//记录操作
void FirmwareWidget::firmwareRecord(fa::icon icon, QString record) {
	QVariantMap optionsLable;
	optionsLable.insert("color", QColor(Qt::black));
	optionsLable.insert("color-active", QColor(Qt::black));
	ui.tipsIconLabel->setPixmap(_awesome->icon(icon, optionsLable).pixmap(QSize(25, 25)));
	ui.downLoadTextTips->setText(record);
}

//总使能slot
void FirmwareWidget::setGetVersionEnable(bool enable) {
	ui.getLocalVersionBtn->setEnabled(enable);
	if (enable) {
		//分别获取boot和app的固件信息
		_config->getJsonDirValue("/system/bootVersion");
		_config->getJsonDirValue("/system/appVersion");
		emit operationRecord(fa::book, tr("获取固件信息"));
	}
}

//自动创建文件夹
void FirmwareWidget::autoCreateDir(QString dir) {
	int error = 0;
    char *dirChar = dir.toLatin1().data();
    if (access(dirChar, 0) == -1) {
#ifdef WIN32
        error = _mkdir(dirChar);
#endif
#ifdef linux
		error = mkdir(dir.c_str(), 0777);
#endif
		if (error == 0) {
			qDebug() << "make Warehouse successfully";
		}
		else {
			qDebug() << "make errorly";
		}
	}
}

//解析固件版本号
QString getRealVersion(double branchName) {
	int branchNum = (int)branchName;
	QString realVersion = QString("%1.%2.%3.%4").arg(branchNum / 1000000).arg((branchNum % 1000000) / 10000).arg((branchNum % 10000) / 100).arg(branchNum % 100);
	return realVersion;
}

//解析app版本号(slot)
void FirmwareWidget::appVersionDecode(JsonDirContent content) {
	double *real = (double *)content.getValue();
	_appVersion = getRealVersion(*real);
	ui.appVersionDisplay->setText(_appVersion);
}

//解析bootloader版本号(slot)
void FirmwareWidget::bootVersionDecode(JsonDirContent content) {
	double *real = (double *)content.getValue();
	_bootVersion = getRealVersion(*real);
	ui.bootVersionDisplay->setText(_bootVersion);
}

//解析下载回复信息
void FirmwareWidget::downloadStringDecode(QByteArray src) {
	QRegExp successExp("!DATA PACKET OK!");
	QString str = QString(src);
	if (str.indexOf(successExp) >= 0) {
		qDebug() << successExp.cap(0);
		sendNextBinFrame();
	}
	QRegExp failExp("!DATA PACKET EORROR!");
	if (str.indexOf(failExp) >= 0) {
		qDebug() << failExp.cap(0);
		resendBinFrame();
	}
}

//获取远程分支信息
void FirmwareWidget::getRemoteBranchList(bool isAll) {
	//尝试连接到远程仓库
	git_remote_callbacks remote_conn_opt = GIT_REMOTE_CALLBACKS_INIT;
	int error = 0;
	const git_error *e = nullptr;
retry:  
	error = git_remote_connect(_remote, GIT_DIRECTION_FETCH, &remote_conn_opt, nullptr, nullptr);
	if (!error) {
		git_remote_head **heads;
		size_t heads_len = 0;
		git_remote_ls((const git_remote_head ***)&heads, &heads_len, _remote);
		//先清除缓存内容
		QList<QString> stringList;
		stringList.clear();
		for (int i = 0; i < heads_len; ++i) {
			QString name = heads[i]->name;
			QRegExp caliExp("refs/heads/(\\d+)");
			if (name.indexOf(caliExp) >= 0) {
				stringList.append(caliExp.cap(1));
			}
		}
		//初始化QtAwesome
		QtAwesome* awesome = new QtAwesome();
		awesome->initFontAwesome();
		QVariantMap options;
		//初始化颜色
		options.insert("color", QColor(0x94ffff));
		options.insert("color-active", QColor(0x94ffff));
		QStandardItemModel *slm = new QStandardItemModel(this);
		slm->setObjectName("slm");
		if (isAll) {
			_branchList.clear();
			for (int i = 0; i < stringList.length(); i++) {
				slm->appendRow(new QStandardItem(awesome->icon(fa::toggleright, options), stringList[i]));
				_branchList = stringList;
			}
		}
		else {
			_branchList.clear();
			uint32_t newestBranch = 0;
			for (int i = 0; i < stringList.length(); i++) {
				newestBranch = qMax<uint32_t>(newestBranch, stringList[i].toUInt());
			}
			_newestBranch = QString("%1").arg(newestBranch);
			_branchList.append(QString("%1").arg(newestBranch));
			slm->appendRow(new QStandardItem(awesome->icon(fa::toggleright, options), _newestBranch));
		}
		ui.remoteFirmwareView->setModel(slm);
	}
	else {
		e = giterr_last();
		qDebug() << "Error: " << error << " / " << e->klass << " : " << e->message;
		if (QMessageBox::No == QMessageBox::critical(this,
			"网络错误",
			"当前无法连接到网络，请检查网络配置，是否重新尝试？",
			QMessageBox::Retry | QMessageBox::Default,
			QMessageBox::No)) {
		}
		else {
			goto retry;
		}
	}
	git_remote_disconnect(_remote);
}

//切换固件选择slot
void FirmwareWidget::selectFirmware(QModelIndex model) {
	_selectBranch = _branchList[model.row()];
	qDebug() << _selectBranch;
	operationRecord(fa::tags, tr("选择固件：") + _selectBranch);
}

//缓存对应固件
void FirmwareWidget::setFirmwareCache() {
	if (_selectBranch == "") {
		QMessageBox::critical(this,
			"错误",
			"请先选择固件！",
			QMessageBox::Yes | QMessageBox::Default);
	}
	else {
		const git_error *e = nullptr;
		int error = 0;
		git_repository* rep = nullptr;
		git_clone_options opt = GIT_CLONE_OPTIONS_INIT;
		QByteArray strArray = _selectBranch.toLatin1();
		opt.checkout_branch = strArray.data();

		firmwareRecord(fa::spinner, tr("固件缓存中。。。"));
		//下载缓存之前必须先清空文件夹里的内容
		QDir dir(basePath);
		dir.removeRecursively();
		//下载固件到缓存
		error = git_clone(&rep, gitPath, basePath, &opt);
		if (error == 0) {
			QString firmwareName;
			//检测文件夹是否存在并自动创建
			autoCreateDir(basePath);
			//将固件存到缓存中
			QDir fileDir(basePath);
			QFileInfoList list = fileDir.entryInfoList(QStringList() << "*.bin");
			firmwareName.append(list.at(0).baseName());
			QFile firmwareFile(QString("%1/%2.bin").arg(basePath).arg(firmwareName));
			firmwareFile.open(QIODevice::ReadOnly);
			QByteArray bin = firmwareFile.readAll();
			firmwareFile.close();
			_totalBinLength = bin.length();
			_binStream = bin;
			_partLength = MAX_PACKET_LENGTH;
			int nCount = (int)qCeil(bin.length() / 1.0 / _partLength);
			_nCountBinNumber = nCount;
			operationRecord(fa::clouddownload, tr("固件缓存成功！"));
			firmwareRecord(fa::clouddownload, tr("缓存成功，固件版本：") + _selectBranch);
			//使能发送按钮
			ui.downloadBtn->setEnabled(true);

			//更新对应更改特性
			QString filename = QString("%1/modification_log.txt").arg(basePath);
			if (filename.isEmpty()) {
				return;
			}
			QFile file(filename);
			if (file.open(QIODevice::ReadOnly)) {
				QByteArray encodedString = file.readAll();
				QTextCodec::ConverterState state;
				QTextCodec *codec = QTextCodec::codecForName("UTF-8");
				QString text = codec->toUnicode(encodedString.constData(),
					encodedString.size(), &state);
				if (state.invalidChars > 0) {
					text = QTextCodec::codecForName("GBK")->toUnicode(encodedString);
				}
				else {
					text = encodedString;
				}
				ui.introdutionBrowser->setPlainText(text);
				file.close();
			}
			//将接收文本框的滚动条滑到最下面
			ui.introdutionBrowser->moveCursor(QTextCursor::End);  
		}
		else {
			e = giterr_last();
			qDebug() << "Error: " << error << " / " << e->klass << " : " << e->message;
			QMessageBox::critical(this,
				"错误",
				"固件文件夹不为空！",
				QMessageBox::Yes | QMessageBox::Default);
			operationRecord(fa::close, tr("固件缓存失败，对应文件夹不为空！"));
			firmwareRecord(fa::close, tr("固件缓存失败，对应文件夹不为空！"));
		}
		git_repository_free(rep);
	}
}

//手动选择本机固件
void FirmwareWidget::manualSelectBin() {
	QFileDialog *fileDialog = new QFileDialog(this);//创建一个QFileDialog对象，构造函数中的参数可以有所添加。
	fileDialog->setWindowTitle(tr("打开文件"));//设置文件保存对话框的标题
	//设置默认文件路径
	fileDialog->setDirectory(".");
	//设置文件过滤器
	fileDialog->setNameFilter(tr("Bin(*.bin)"));
	//设置选择一个文件QFileDialog::ExistingFile
	fileDialog->setFileMode(QFileDialog::ExistingFile);
	//设置视图模式
	fileDialog->setViewMode(QFileDialog::Detail);
	if (fileDialog->exec() == QDialog::Accepted) {
		//得到用户选择的第一个文件目录
		QString path = fileDialog->selectedFiles()[0];
		fileDialog->close();
		//从目录中抓取文件
		QFile firmwareFile(path);
		firmwareFile.open(QIODevice::ReadOnly);
		QByteArray bin = firmwareFile.readAll();
		firmwareFile.close();
		_totalBinLength = bin.length();
		_binStream = bin;
		_partLength = MAX_PACKET_LENGTH;
		int nCount = (int)qCeil(bin.length() / 1.0 / _partLength);
		_nCountBinNumber = nCount;
		operationRecord(fa::clouddownload, tr("手动加载固件成功！"));
		firmwareRecord(fa::clouddownload, tr("手动加载固件成功！"));
		//使能发送按钮
		ui.downloadBtn->setEnabled(true);
	}
	else {
		
	}
	delete fileDialog;
}

//固件打包函数
QByteArray package(QByteArray sendBytes, int totalSize, int currentNumber) {
	int dataLength = sendBytes.length();
	//如果用8CRC位校验，则末尾+1，16CRC位+2，32CRC位+4
	int frameLen = 3 + 3 + dataLength + 4;
	QByteArray frameBytes;
	frameBytes.resize(frameLen);
	frameBytes[0] = (uint8_t)'#';
	frameBytes[1] = (uint8_t)(totalSize - currentNumber);
	frameBytes[2] = (uint8_t)currentNumber;
	frameBytes[3] = (uint8_t)'&';
	frameBytes[4] = (uint8_t)(dataLength % 256);
	frameBytes[5] = (uint8_t)(dataLength / 256);
	memcpy(frameBytes.data() + 6, sendBytes.constData(), dataLength);
	//此处使用32位CRC校验
	CRC::AppendCrc32CheckSum((uint8_t *)frameBytes.data(), frameLen);
	return frameBytes;
}

//发送下一帧内容
void FirmwareWidget::sendNextBinFrame() {
	_partLength = MAX_PACKET_LENGTH;
	int offset = 0;
	int length = 0;
	if ((_currentBinNumber)* _partLength < _totalBinLength) {
		offset = (_currentBinNumber - 1) * _partLength;
		length = _partLength;
	}
	else {
		offset = (_currentBinNumber - 1) * _partLength;
		length = _totalBinLength - offset;
	}
	QByteArray partArray;
	partArray.resize(length);
	//copy
	memcpy(partArray.data(), _binStream.constData() + (_currentBinNumber - 1) * _partLength, length);
	//add head + crc
	_currentSendFrame = package(partArray, _nCountBinNumber, _currentBinNumber);
	//send packet
	_connectThread->appendTx(&_currentSendFrame);
	//进度条更新
	int progressValue = (int)((_currentBinNumber) / 1.0 / _nCountBinNumber * 100);
	ui.downloadProgressBar->setValue(progressValue);
	//下载完毕触发自动重连
	if (progressValue == 100) {
		//50ms后自动断开，并触发自动重连
		_reconnectTimerId = this->startTimer(50);
	}
	if (_currentBinNumber < _nCountBinNumber) {
		_currentBinNumber++;
	}
	else {
		_currentBinNumber = 1;
		//_nCountBinNumber = 0;
		//_sendPressed = false;
		//_finishJob = true;
	}
}

//重发当前帧
void FirmwareWidget::resendBinFrame() {
	_connectThread->appendTx(&_currentSendFrame);
}

//定时器触发函数
void FirmwareWidget::timerEvent(QTimerEvent *event) {
	//app --> boot 跳转重连
	if (event->timerId() == _startDownloadTimerId) {
		//自动发送重连定时器相应
		if (!_connectThread->isRunning()) {
			_connectThread->openPort(_reconnectPortName, _reconnectBaudrate);
			emit reConnectPort(true, false);
			QObject::connect(_connectThread, SIGNAL(rawStringDecode(QByteArray)), this, SLOT(downloadStringDecode(QByteArray)));
			//发送第一帧
			sendNextBinFrame();
			this->killTimer(_startDownloadTimerId);
			qDebug() << "start download!";
			_startDownloadTimerId = 0;
			operationRecord(fa::edit, tr("开始下载"));
			firmwareRecord(fa::edit, tr("开始下载"));
		}
		else {
			_reconnectPortName = _connectThread->getPortName();
			_reconnectBaudrate = _connectThread->getBaudrate();
			//关闭串口
			_connectThread->stop();
			emit reConnectPort(false, false);
			this->killTimer(_startDownloadTimerId);
			//400ms后自动重连
			_startDownloadTimerId = this->startTimer(400);
		}
	}
	//boot --> app跳转重连
	if (event->timerId() == _reconnectTimerId) {
		//自动发送重连定时器相应
		if (!_connectThread->isRunning()) {
			_connectThread->openPort(_reconnectPortName, _reconnectBaudrate);
			emit reConnectPort(true, true);
			QObject::disconnect(_connectThread, SIGNAL(rawStringDecode(QByteArray)), this, SLOT(downloadStringDecode(QByteArray)));
			this->killTimer(_reconnectTimerId);
			qDebug() << "auto reconnect!";
			ui.downloadProgressBar->setValue(0);
			_reconnectTimerId = 0;
			operationRecord(fa::download, tr("下载完成！"));
			firmwareRecord(fa::download, tr("下载完成！"));
		}
		else {
			//打开自动重连
			_reconnectPortName = _connectThread->getPortName();
			_reconnectBaudrate = _connectThread->getBaudrate();
			//关闭串口
			_connectThread->stop();
			emit reConnectPort(false, true);
			this->killTimer(_reconnectTimerId);
			//500ms后自动重连
			_reconnectTimerId = this->startTimer(500);
		}
	}
}

//下载触发
void FirmwareWidget::downLoad() {
	if (_connectThread->isRunning()) {
		//失能发送按钮
		ui.downloadBtn->setEnabled(false);
		//回到boot段
		_currentBinNumber = 1;
		//发送更新固件的json
		bool boolean = true;
		_config->sendJsonDirValue("/system/update", &boolean);
		//等自动重连
		_startDownloadTimerId = this->startTimer(50);
	}
	else {
		QMessageBox::critical(this,
			"错误",
			"请先打开串口！",
			QMessageBox::Yes | QMessageBox::Default);
	}
}

//按键更新总slot
void FirmwareWidget::firmwareClicked() {
	QPushButton *btn = (QPushButton *)sender();
	//获取设备信息
	if (btn->objectName() == ui.getLocalVersionBtn->objectName()) {
		//分别获取boot和app的固件信息
		_config->getJsonDirValue("/system/bootVersion");
		_config->getJsonDirValue("/system/appVersion");
		operationRecord(fa::book, tr("获取固件信息"));
	}
	//获取最新固件
	else if (btn->objectName() == ui.getLastedBinBtn->objectName()) {
		getRemoteBranchList(false);
		_selectBranch = _newestBranch;
		operationRecord(fa::tags, tr("选择固件：") + _selectBranch);
	}
	//获取远程历史分支列表
	else if (btn->objectName() == ui.getPreviousBinBtn->objectName()) {
		getRemoteBranchList(true);
		operationRecord(fa::history, tr("获取过往固件"));
	}
	//加载用户固件
	else if (btn->objectName() == ui.loadCustomBtn->objectName()) {
		manualSelectBin();
	}
	//缓存固件
	else if (btn->objectName() == ui.chooseFirmwareBtn->objectName()) {
		setFirmwareCache();
	}
	//下载固件
	else if (btn->objectName() == ui.downloadBtn->objectName()) {
		downLoad();
	}
}

//自动更新的开关
void FirmwareWidget::autoUpdateClicked(bool clicked) {

}
